// 这是一个数据分析展示的页面的script标签内容，后端接口只返回时间段内数据，前端接收到数据进行分析和展示
// 此页面为原生js开发，特点是功能封装、功能业务逻辑严密、语法精湛，牵涉面广 。亮点是echarts图表定制、下拉表互斥联动、数学计算及数据格式化、原型链扩展。 主要开发思想是opp、组件化   
 
 
 //- 等待dom完成后操作
 $(function(){
  render()  //- 初始化渲染
})

//- 定义全局变量
let l = console.log
let chartQueue = []           // echarts实例队列
let application = {}          // 下拉表数据
let adp = {}                  // 下拉表数据
let error = []                // 错误信息捕获数组
let selectDatas = null        // 缓存下拉表接口数据
let consoleExit = false       // 关闭控制台输出
consoleExit && ( l = new Function() )            

//- 页面渲染函数
// @param 获取数据参数，搜索时应用，这个参数用于选择性渲染下拉表
// @isPublisher 是否是媒体主，搜索时应用，这个参数用于聚合数据结构变换
async function render(param, isPublisher){

  //- 数据获取和格式化处理
  $('#mymask').show()
  $("[class*='sumRate']").html("")
  $('.opBtn a').toggleClass('disabled')   // 开启搜索按钮禁用状态
  let datas = await getData(param)

  //- $('#mymask').hide()
  //- $('.opBtn a').toggleClass('disabled')
  //- l(datas)
  //- return 
  let selects = selectDatas || await getSelect()
  Object.assign(datas.result,selects.result)
  l('接口返回数据：',datas)
  $('#mymask').hide()
  if(!datas.result.apiAggregations || datas.result.apiAggregations.length<1) return alert('接口无返回数据！！！')
  const { channel, publisher, avgData, yestodayData, sumEle1, sumEle2 } = disposeData(datas, isPublisher)
  if(!yestodayData) return alert('当前日期无数据！！！')
  const { adid_total, carrier_total, idfa_total, imei_total, imsi_total, ip_total, mac_total, network_total, oaid_total, os_type_total, total } = yestodayData
  channel.unshift({name:'空',val:0})
  publisher.unshift({name:'空',val:0})
  const failAnalsisChartData = [adid_total, carrier_total, idfa_total, imei_total, imsi_total, ip_total, mac_total, network_total, oaid_total, os_type_total, total]
  const failAnalsisChartName = ['adid_total','carrier_total','idfa_total','imei_total','imsi_total','ip_total','mac_total','network_total','oaid_total','os_type_total','total']
  

  //- 下拉表渲染,搜索时不渲染
  if(!param){
    renderSelect('.channel',channel)
    renderSearchSelect('.mmedia',publisher)
    renderSelect('.application',[{name: '请选择媒体主',val:0}])
    renderSelect('.adp',[{name: '请选择应用',val:0}])
  }

  //- 卡片渲染
  renderCard('.sumRate',sumEle1)
  renderCard('.sumRate2',sumEle2)
  
  //- 图表渲染和自适应事件绑定
  if(isPublisher){
    renderChart('#requestChart',yestodayData.uprequest_totle,avgData.uprequest_totle_wave,'上游请求趋势图','请求数', '波动指数')
  }else{
    renderChart('#requestChart',yestodayData.request_totle,avgData.request_totle_wave,'下游请求趋势图','请求数', '波动指数')
  }
  renderChart('#responseChart',yestodayData.upresponse_time,avgData.upresponse_time_wave,'上游平均响应趋势图','平均响应时长', '波动指数','ms')
  renderChart('#responseChart1',yestodayData.downresponse_time,avgData.downresponse_time_wave,'下游平均响应趋势图','平均响应时长', '波动指数','ms')
  renderChart('#timeoutChart',yestodayData.uptimeout_totle,avgData.uptimeout_totle_wave,'上游超时趋势图','超时数', '波动指数')
  renderChart('#timeoutChart1',yestodayData.downtimeout_totle,avgData.downtimeout_totle_wave,'下游超时趋势图','超时数', '波动指数')
  //- renderChart('#failChart',yestodayData.upfailTotle,avgData.upfailTotle,'上游错误趋势图','错误数', '波动指数')
  //- renderChart('#failAnalsisChart',arr,arr1,'上游错误分析图','缺失', '波动指数')
  renderChart('#failChart1',yestodayData.downfailTotle,avgData.downfailTotle_wave,'下游错误趋势图','错误数', '波动指数')
  renderLineChart('#failAnalsisChart1',failAnalsisChartData,'下游错误分析图',failAnalsisChartName)
  renderChart('#fillChart',yestodayData.fillRateTotle,avgData.fillRateTotle_wave,'填充率波动图','填充率', '波动指数','%')
  renderChart('#exposeChart',yestodayData.exposeRateTotle,avgData.exposeRateTotle_wave,'曝光率波动图','曝光率', '波动指数','%')
  renderChart('#clickChart',yestodayData.clickRateTotle,avgData.clickRateTotle_wave,'点击率波动图','点击率', '波动指数','%')
  chartResize()
  $('.opBtn a').toggleClass('disabled')   // 关闭搜索按钮禁用状态
}

//- 搜索方法
function mysearch(){

  //- 筛选参数获取
  let yestoday = new Date().getFullYear()+'-'+(new Date().getMonth()+1).toString().padStart(2,'0')+'-'+(new Date().getDate()-1).toString().padStart(2,'0')
  let channel = $('.channel').val()
  let application = $('.application').val()
  let adp = $('.adp').val()
  let date = $('.date').val()
  if(date.replaceAll('-','') > Number(yestoday.replaceAll('-',''))) return alert('选择日期不能大于昨天日期！！！')
  
  //- 请求参数构造
  let param = {channel:channel, appId:application, posId:adp, date: date}
  channel == '0' && (delete param.channel)
  application == '0' && (delete param.appId)
  adp == '0' && (delete param.posId)
  date == yestoday && (delete param.date)
  //- l(channel,application,adp,date,param)

  //- 无效搜索过滤
  if(Object.keys(param).length == 0) return alert('请填写搜索参数，媒体主不算搜索参数！！！')
  $('#quest-tab').click()     // 标签页复位
  if(param.channel){
    render(param, 1)
  }else{
    render(param)
  }
}

//- 异步数据请求
function getData(param){
  const url = '/admins/market_monitor/getdata'
  let datas = $.get(url,param)
  return datas
}
async function getSelect(){
  const url = '/admins/market_monitor/getselect'
  let datas = await $.get(url)
  selectDatas = datas
  return datas
}

//- 下拉表渲染函数
function renderSelect(ele,datas){
  if(!ele || !datas) return err.push({errmsg:'下拉表渲染错误',ele:ele,datas:datas})
  let html = []
  datas.map(v=>{
    html.push( `<option value=${v.val}>${v.name}</option>`)
  })      
  $(ele).html(html.join(""))
}

//- 搜索下拉表渲染函数
function renderSearchSelect(ele,datas){
  if(!ele || !datas) return err.push({errmsg:'下拉表渲染错误',ele:ele,datas:datas})
  let html = []
  datas.map(v=>{
    html.push( `<option value=${v.name}>`)
  })      
  $(ele).html(html.join(""))
}

//- 媒体和广告主下拉框互斥联动事件
function rejectChange(text){
  let value = $(event.target).val()
  l(value)
  if(text == 'channel'){
    $('#mmedia').val('空')
    renderSelect('.application',[{name: '请选择媒体主',val:0}])
    renderSelect('.adp',[{name: '请选择应用',val:0}])
  }else if(text == 'publisher'){
    $('.channel').val(0)
    value = value.match(/\(.*\)/g) && value.match(/\(.*\)/g)[0].slice(1,-1)
    application[value] && (application[value].unshift({name: '空',val:0}))
    renderSelect('.application', application[value] || [{name: '该媒体主无应用',val:0}])
    renderSelect('.adp',[{name: '请选择应用',val:0}])
  }else{
    if(value == '0'){
      renderSelect('.adp',[{name: '请选择应用',val:0}])
    }else{
      renderSelect('.adp', adp[value] || [{name: '该应用无广告位',val:0}])
    }
  }
}

//- 柱状折线融合图表渲染函数
function renderChart(ele, barData=[], lineData=[], title='title', barName='bar', lineName='line', unit=''){
  let dom = $(ele)[0]      
  if(!ele) return err.push({errmsg:'图表渲染找不到容器',ele:ele})
  //- if(unit == 'k'){barData = barData.map(v=>{return Math.round(v/1000)})}
  let myChart = echarts.init(dom)
  let xData = Array(24).fill(0).map((v,i)=>{return i.toString().padStart(2,'0')+'时'})
  let blueDiv = `<span style="display:inline-block;margin-right:5px;margin-top:10px;border-radius:5%;width:10px;height:10px;left:5px;background-color:rgb(101,151,246);"></span>`
  let greenDiv = `<span style="display:inline-block;margin-right:5px;border-radius:5%;width:10px;height:2px;left:5px;background-color:rgb(118,218,176);vertical-align: middle;"></span>`
  let option = {
    title: {text: title,top: '-2%'},
    color: ['#6597f6','#76dab0'],
    tooltip: {trigger: 'axis',backgroundColor:'#fff',textStyle:{color:'#777',fontSize:'10',fontWeight:600},formatter:function(params) {let line = params[1];let bar = params[0];return `${line.name}</br> ${blueDiv}${bar.seriesName}:&emsp;&emsp;${trans(bar.data)}${unit}</br>${greenDiv}${line.seriesName}:&emsp;&emsp;${line.data}%`},borderColor:'#ececec',borderWidth:1,padding:10},
    grid: {left: '5%',right: '8%',bottom: '8%'},
    legend: {data: [barName, lineName],right:'20%'},
    xAxis: {type: 'category',data: xData,axisLabel:{interval:0,fontSize:'10',color:'#888'},axisLine:{show:false},axisTick:{show:false}},
    yAxis: [
      {type: 'value',boundaryGap:['0%','10%'],axisLabel:{interval:0,fontSize:'10',color:'#888',formatter: function(value) {return (Math.abs(value)>10000 ? value/1000 + 'k' + unit : value + unit)}},axisLine:{show:false},axisTick:{show:false},splitNumber:5},
      {type: 'value',boundaryGap:['10%','10%'],axisLabel:{interval:0,fontSize:'10',color:'#888',formatter:'{value}%'},axisLine:{show:false,onZero:true},axisTick:{show:false},splitLine:{show:false},splitNumber:5}
    ],
    series: [
      {data: barData,type: 'bar',name: barName},
      {data: lineData,type: 'line',name: lineName,yAxisIndex: 1}
    ]
  }
  myChart.setOption(option)
  chartQueue.push(myChart)
}

//- 折线复合图表渲染函数
function renderLineChart(ele, lineData=[], title='title', lineName=[]){
  let dom = $(ele)[0]      
  if(!ele) return err.push({errmsg:'图表渲染找不到容器',ele:ele})
  let myChart = echarts.init(dom)
  let xData = Array(24).fill(0).map((v,i)=>{return i.toString().padStart(2,'0')+'时'})
  let blueDiv = `<span style="display:inline-block;margin-right:5px;margin-top:10px;border-radius:5%;width:10px;height:10px;left:5px;background-color:rgb(101,151,246);"></span>`
  let greenDiv = `<span style="display:inline-block;margin-right:5px;border-radius:5%;width:10px;height:2px;left:5px;background-color:rgb(118,218,176);vertical-align: middle;"></span>`
  let option = {
    title: {text: title,top: '-2%'},
    color: ['#6597f6','#76dab0','#687897','#f5c041','#e97962','blue','red','purple','green','pink','green'],
    tooltip: {trigger: 'axis',backgroundColor:'#fff',textStyle:{color:'#777',fontSize:'10',fontWeight:600},borderColor:'#ececec',borderWidth:1,padding:10},
    grid: {left: '4%',right: '8%',bottom: '8%'},
    legend: {data: lineName,right:'20%',left: '18%',top:'2%'},
    xAxis: {type: 'category',data: xData,axisLabel:{interval:0,fontSize:'10',color:'#888'},axisLine:{show:false},axisTick:{show:false}},
    yAxis: {type: 'value',boundaryGap:['10%','10%'],axisLabel:{interval:0,fontSize:'10',color:'#888',formatter: function(value) {return (Math.abs(value)>10000 ? value/1000 + 'k' : value)}},axisLine:{show:false},axisTick:{show:false},splitNumber:5},
    series: []
  }
  option.series = []
  lineData.map((v,i) => {option.series.push({data: v,type: 'line',name: lineName[i]})})
  myChart.setOption(option)
  chartQueue.push(myChart)
}

//- 图表尺寸自适应函数
function chartResize(){
  window.addEventListener("resize",function (){
    chartQueue.map(v=>{
      v.resize()
    })
  })
}

//- 图表尺寸重绘
function tab(num){    
  let index = num-1
  setTimeout(function(){
    num ==1 && chartQueue[index+0].resize() 
    num ==2 && chartQueue[index+0].resize() 
    num ==2 && chartQueue[index+1].resize() 
    num ==3 && chartQueue[index+1].resize() 
    num ==3 && chartQueue[index+2].resize() 
    //- num ==4 && chartQueue[index+2].resize() 
    //- num ==4 && chartQueue[index+3].resize() 
    num ==4 && chartQueue[index+4-2].resize() 
    num ==4 && chartQueue[index+5-2].resize() 
    num ==5 && chartQueue[index+5-2].resize() 
    num ==5 && chartQueue[index+6-2].resize() 
    num ==5 && chartQueue[index+7-2].resize() 
  },180)  
}

//- 数据卡片渲染
function renderCard(ele,data){
  let html = []
  let txt = ''
  data.map((v,i)=>{
    txt += `<div class="table-bordered mr-1 ml-1 mycard pt-3 pl-5 ">
              <div class="cardname">${v.name}</div>
              <div class="row">`
    if(v.name.match('时长')){
      txt += `<div class="col-8 cardnum">${trans(v.num)}<span class="small">ms</span></div>`
    }else if(v.name.match('率')){
      txt += `<div class="col-8 cardnum">${v.num}<span class="small">%</span></div>`
    }else{
      txt += `<div class="col-8 cardnum">${trans(v.num)}</div>`
    }
    if(v.rate>0){
      txt += `<div class="col-4 cardrate red mb-2">${v.rate || 0}<span class="small">%</span></div>`
    }else{
      txt += `<div class="col-4 cardrate green mb-2">${typeof v.rate == 'number' ? Math.abs(v.rate) : 0}<span class="small">%</span></div>`
    }
    txt += `</div></div>`
    html.push(txt)
    txt = ''
  })
  $(ele).html(html.join(""))
}

//- 数字用逗号分割
function trans(num){
  if(String(num).match(/\./)) return num   // 小数绕过分割
  let isnum = typeof num == 'number'
  if(isnum){
    num = String(num).split("").reverse().map((v,i)=>{
      if(i%3==0 && i!=0){
        return v+','
      }else{
        return v
      }
    }).reverse().join("")
  }
  num = isnum ? num : 0
  return num 
}

//- 数据格式化处理
function disposeData(data, isPublisher){
  let yestoday = $('.date').val()
  const { apiAggregations, applications, channels, publishers, yjhPositions, downstreamFact} = data.result
  
  //- 下拉表数据格式化处理
  let channel = channels.map((v,i)=>{return {name:v,val:i+1}})
  let publisher = publishers.map((v,i)=>{return {name:(v.name || '')+'('+v.user_id+')',val:v.user_id}})
  applications.map(v=>{application[v.owner_id] ? application[v.owner_id].push({name:v.app_name,val:v._id}) : (application[v.owner_id] = [{name:v.app_name,val:v._id}])})
  yjhPositions.map(v=>{adp[v.app_id] ? adp[v.app_id].push({name:v.name,val:v._id}) : (adp[v.app_id] = [{name:v.name,val:v._id}])})
  
  //- 聚合数据和错误分析设计按天按小时分类聚合处理
  let dataByDay = {}
  apiAggregations.concat(downstreamFact).map(v=>{     
    let dates = v.hourly.split(" ")
    if(dates.length<2) return error.push({errmsg:'日期错误的小时聚合数据',val:v})
    let date = dates[0]
    let hour = +dates[1].slice(0,2)   

    if(dataByDay[date]){
      fill(dataByDay[date],v,hour)
    }else{
      dataByDay[date] = getDataStruture()
      fill(dataByDay[date],v,hour)
    }
  })

  //- 聚合数据按天按小时分类聚合处理 上下游响应时间数组转平均数
  for(k in dataByDay){
    dataByDay[k].upresponse_time = dataByDay[k].upresponse_time.map(v=>{if(Array.isArray(v)){return v.avg()}else{return v}})
    dataByDay[k].downresponse_time = dataByDay[k].downresponse_time.map(v=>{if(Array.isArray(v)){return v.avg()}else{return v}})
  }
  l('分类后的按天聚合数据：',dataByDay)

  //- 前n天平均数计算
  let avgData = getOldAvg(dataByDay)
  l('前n天平均数据：',avgData)

  //- 获取昨天数据
  let yestodayData = dataByDay[yestoday]
  if(!yestodayData) return {}
  l('昨天数据：',yestodayData)

  //- 图表数据计算
  chartConclude(yestodayData)
  chartConclude(avgData)
  l('添加图表数据计算后的数据(yestoday,avg)：',yestodayData,avgData)

  //- 波动指数计算
  waveConclude(yestodayData,avgData)
  l('波动指数计算后的前n天平均数据：',avgData)

  //- 卡片数据计算
  let sumEle1 = [
    {name:'媒体总请求量',num:yestodayData.request_totle.sum(),rate: formatRate(yestodayData.request_totle.sum(),avgData.request_totle.sum(),0,1)},
    {name:'上游平均响应时长',num:yestodayData.upresponse_time.avg(),rate: formatRate(yestodayData.upresponse_time.avg(),avgData.upresponse_time.avg(),0,1)},
    {name:'媒体平均应答时长',num:yestodayData.downresponse_time.avg(),rate: formatRate(yestodayData.downresponse_time.avg(),avgData.downresponse_time.avg(),0,1)},
    {name:'超时数',num:yestodayData.downtimeout_totle.sum(),rate: formatRate(yestodayData.downtimeout_totle.sum(),avgData.downtimeout_totle.sum(),0,1)}]
  let sumEle2 = [
    {name:'下游错误数',num:yestodayData.downfailTotle.sum(),rate: formatRate(yestodayData.downfailTotle.sum(),avgData.downfailTotle.sum(),0,1)},
    {name:'填充率',num:formatRate(yestodayData.fill_totle.sum(),sumEle1[0].num,0,0,3),rate: formatRate(yestodayData.fillRateTotle.avg(),avgData.fillRateTotle.avg(),0,1)},
    {name:'曝光率',num:formatRate(yestodayData.expose_totle.sum(),yestodayData.fill_totle.sum(),0,0,3),rate: formatRate(yestodayData.exposeRateTotle.avg(),avgData.exposeRateTotle.avg(),0,1)},
    {name:'点击率',num:formatRate(yestodayData.click_totle.sum(),yestodayData.expose_totle.sum(),0,0,3),rate: formatRate(yestodayData.clickRateTotle.avg(),avgData.clickRateTotle.avg(),0,1)}]
  if(isPublisher){
    sumEle1[0] = {name:'媒体总请求量',num:yestodayData.uprequest_totle.sum(),rate: formatRate(yestodayData.uprequest_totle.sum(),avgData.uprequest_totle.sum(),0,1)}
    sumEle1[3] = {name:'超时数',num:yestodayData.uptimeout_totle.sum(),rate: formatRate(yestodayData.uptimeout_totle.sum(),avgData.uptimeout_totle.sum(),0,1)}
  }
  l('汇总的卡片数据：',sumEle1,sumEle2)

  return { channel, publisher, avgData, yestodayData, sumEle1, sumEle2 } 
}

//- 数据结构填充
function fill(obj,data,hour){
  
  const { action, ms_spent, ms_timeout_hit, res_code, res_status, total_hit, field} = data 

  //- 下游请求填充sum值
  if(action == 255){
    let val = obj.request_totle[hour]
    obj.request_totle[hour] = val>0 ? (val+total_hit) : total_hit
  }

  //- 上游请求填充sum值
  if(action == 254){
    let val = obj.uprequest_totle[hour]
    obj.uprequest_totle[hour] = val>0 ? (val+total_hit) : total_hit
  }

  //- 上下游响应时间数组构建（只统计成功请求的值）
  if(action == 254 && res_code == 1 && res_status == 0){
    let val = obj.upresponse_time[hour]
    Array.isArray(val) ? val.push(ms_spent) : (obj.upresponse_time[hour] = [ms_spent])
  }
  if(action == 255 && res_code == 0 && res_status == 0){
    let val = obj.downresponse_time[hour]
    Array.isArray(val) ? val.push(ms_spent) : (obj.downresponse_time[hour] = [ms_spent])
  }
  //- 上下游超时sum值
  if(action == 254){
    let val = obj.uptimeout_totle[hour]
    obj.uptimeout_totle[hour] = val>0 ? (val+ms_timeout_hit) : ms_timeout_hit
  }
  if(action == 255){
    let val = obj.downtimeout_totle[hour]
    obj.downtimeout_totle[hour] = val>0 ? (val+ms_timeout_hit) : ms_timeout_hit
  }

  //- 曝光sum值
  if(action == 3){
    let val = obj.expose_totle[hour]
    obj.expose_totle[hour] = val>0 ? (val+total_hit) : total_hit
  }

  //- 点击sum值
  if(action == 4){
    let val = obj.click_totle[hour]
    obj.click_totle[hour] = val>0 ? (val+total_hit) : total_hit
  }

  //- 上游返回值错误统计  500-600属于下游请求错误，1000+属于上游返回错误 (错误分析没展示这个数据分析，以后可能会加这个纬度数据)
  //- if([512,513,514,515,1024,1280,1281,1282,1283,1284,1285,1286,1535].includes(res_status)){
  //- if([1280].includes(res_status)){
  //- 填充sum值
  if(action == 253){
    let val = obj.fill_totle[hour]
    obj.fill_totle[hour] = val>0 ? (val+total_hit) : total_hit
  }

  //- 下游错误错误分析sum值
  if(field){
    let val = obj[field][hour]
    obj[field][hour] = val>0 ? (val+total_hit) : total_hit
  }
}

//- 前n天平均数计算  目前后端控制天数(todays参数没用到)，本函数计算除昨天外所有天数据平均数
function getOldAvg(data, todays){
  let yestoday = $('.date').val()
  let avgData = getDataStruture()
  let days = Object.keys(data).filter(v=>{return v != yestoday})
  
  //- 第一层数据结构键遍历，第二层每个键24小时时间遍历，第三层统计前n天数据遍历
  for(let k in avgData){
    for(let i = 0; i<24; i++){
      let result = []
      for(let j = 0; j<days.length; j++){
        let oldday = days[j]
        result.push(data[oldday][k][i])
      }
      avgData[k][i] = result.avg()
    }
  }
  return avgData
} 

//- 图表数据计算
function chartConclude(data){

  //- 错误图表计算
  let downCode = ['adid_total','carrier_total','idfa_total','imei_total','imsi_total','ip_total','mac_total','network_total','oaid_total','os_type_total','total']

  let downfailTotle = []
  
  for(let i = 0; i<24; i++){
    let downerr = []
    for(let j = 0; j<downCode.length; j++){
      let ele = data[downCode[j]][i]
      downerr.push(ele)
    }
    downfailTotle[i] = downerr.sum()
  }

  //- ++++++++下游错误数据挂载
  data.downfailTotle = downfailTotle


  //- 上报图表计算
  let fillRateTotle = []
  let exposeRateTotle = []
  let clickRateTotle = []

  for(let i = 0; i<24; i++){
    let fillR = formatRate(data.fill_totle[i],data.request_totle[i],0,0,3)
    let exposeR = formatRate(data.expose_totle[i],data.fill_totle[i],0,0,3)
    let clickR = formatRate(data.click_totle[i],data.expose_totle[i],0,0,3)
    fillRateTotle.push(fillR)
    exposeRateTotle.push(exposeR)
    clickRateTotle.push(clickR)
  }

  //- ++++++++上报图表数据挂载
  data.fillRateTotle = fillRateTotle
  data.exposeRateTotle = exposeRateTotle
  data.clickRateTotle = clickRateTotle
}

//- 波动指数计算
function waveConclude(yestodayData,avgData){
  let concludeFeild = ['request_totle','uprequest_totle','upresponse_time','downresponse_time','uptimeout_totle','downtimeout_totle','downfailTotle','fillRateTotle','exposeRateTotle','clickRateTotle']
  for(let i = 0; i < concludeFeild.length; i++){
    let feild = concludeFeild[i]
    avgData[feild+'_wave'] = []
    for(let j = 0; j < 24; j++){
      avgData[feild+'_wave'].push(formatRate(yestodayData[feild][j],avgData[feild][i],0,1))
    }
  }
}

//- 百分数计算（分子，分母，是否是补数，是否是涨跌幅度, 保留小数位（默认不保留））
function formatRate(numerator, denominator, isComplement, isIncrement, fixedNum=0){
  let result = 0
  if(Number.isFinite(numerator) && Number.isFinite(denominator)){
    result = +(numerator/denominator*100).toFixed(fixedNum)
  }else{
    err.push({msg:'百分数格式化错误',numerator, denominator, isComplement, isIncrement})
    return result
  }
  isComplement && (result = 100 - result)
  isIncrement && (result = result - 100)
  if(!Number.isFinite(result)) return 0
  return result
}

// 定义数据分析数据结构
function getDataStruture(){
  let dataByDayStruture = {
    request_totle:Array(24).fill(0),       // 下游请求数总数统计
    uprequest_totle:Array(24).fill(0),     // 上游请求数总数统计
    upresponse_time: Array(24).fill(0),    // 上游响应时间平均数统计
    downresponse_time: Array(24).fill(0),  // 下游响应时间平均数统计
    uptimeout_totle: Array(24).fill(0),    // 上游超时数总数统计
    downtimeout_totle: Array(24).fill(0),  // 下游超时数总数统计
    fill_totle: Array(24).fill(0),         // 填充总数统计
    expose_totle: Array(24).fill(0),       // 曝光总数统计
    click_totle: Array(24).fill(0),        // 点击总数统计
    adid_total: Array(24).fill(0),             // 下游错误分析同名字段 
    carrier_total: Array(24).fill(0),          // 下游错误分析同名字段 
    idfa_total: Array(24).fill(0),             // 下游错误分析同名字段 
    imei_total: Array(24).fill(0),             // 下游错误分析同名字段 
    imsi_total: Array(24).fill(0),             // 下游错误分析同名字段 
    ip_total: Array(24).fill(0),               // 下游错误分析同名字段 
    mac_total: Array(24).fill(0),              // 下游错误分析同名字段 
    network_total: Array(24).fill(0),          // 下游错误分析同名字段 
    oaid_total: Array(24).fill(0),             // 下游错误分析同名字段 
    os_type_total: Array(24).fill(0),          // 下游错误分析同名字段 
    total: Array(24).fill(0),                  // 下游错误分析同名字段 
  
  }
  return JSON.parse(JSON.stringify(dataByDayStruture))
}

//- 数组扩展sum方法
Array.prototype.sum = function(){
  let result = 0
  for (var i = 0; i < this.length; i++) {
    if(typeof this[i] != 'number') continue
    result += this[i]
  }
  return result
}

//- 数组扩展average方法
Array.prototype.avg = function(num=0){
  let sum = this.sum()
  let len = this.length || 1
  let result = (sum/len).toFixed(num)
  return +result
}